import re


# ======================== 1. 文本块分割函数 ========================
def lines(file):
    for line in file:
        yield line
    yield '\n'


def blocks(file):
    block = []
    for line in lines(file):
        if line.strip():
            block.append(line.rstrip('\n'))
        elif block:
            yield '\n'.join(block).strip()
            block = []
    if block:
        yield '\n'.join(block).strip()


# ======================== 2. 规则基类 + 具体规则 ========================
class Rule:
    type = None

    def condition(self, block):
        raise NotImplementedError

    def action(self, block, handler):
        handler.start(self.type)
        handler.feed(block)
        handler.end(self.type)
        return True


class TitleRule(Rule):
    type = 'title'
    first = True

    def condition(self, block):
        if not self.first:
            return False
        self.first = False
        return not '\n' in block and len(block) <= 70 and not block.endswith(
            ':')

    def action(self, block, handler):
        handler.start(self.type)
        handler.feed(block)
        handler.end(self.type)
        return True


class HeadingRule(Rule):
    type = 'heading'

    def condition(self, block):
        return not '\n' in block and len(block) <= 70 and not block.endswith(
            ':')

    def action(self, block, handler):
        handler.start(self.type)
        handler.feed(block)
        handler.end(self.type)
        return True


class ListItemRule(Rule):
    type = 'listitem'

    def condition(self, block):
        return block.startswith('- ')  # 严格匹配 "- " 开头

    def action(self, block, handler):
        handler.start(self.type)
        handler.feed(block[2:].strip())  # 去除 "- "
        handler.end(self.type)
        return True


class ListRule(Rule):
    type = 'list'
    inside = False

    def condition(self, block):
        return True

    def action(self, block, handler):
        if not self.inside and ListItemRule().condition(block):
            handler.start(self.type)
            self.inside = True
        elif self.inside and not ListItemRule().condition(block):
            handler.end(self.type)
            self.inside = False
        return False  # 不终止，让 ListItemRule 处理


class ParagraphRule(Rule):
    type = 'paragraph'

    def condition(self, block):
        return True

    def action(self, block, handler):
        handler.start(self.type)
        handler.feed(block)
        handler.end(self.type)
        return True


# ======================== 3. Handler 基类 + HTML 渲染 ========================
class Handler:
    def callback(self, prefix, name, *args):
        method = getattr(self, f"{prefix}{name}", None)
        return method(*args) if callable(method) else None

    def start(self, name):
        self.callback('start_', name)

    def end(self, name):
        self.callback('end_', name)

    def sub(self, name):
        def substitution(match):
            result = self.callback('sub_', name, match)
            return result if result is not None else match.group(0)

        return substitution


class HTMLRender(Handler):
    def __init__(self):
        self.html = []
        self.inside_list = False  # 辅助判断列表嵌套

    def start_document(self):
        self.html.append(
            '<!DOCTYPE html>\n<html>\n<head>\n<meta charset="utf-8">\n<title>World Wide Spam</title>\n</head>\n<body>')

    def end_document(self):
        self.html.append('\n</body>\n</html>')

    def start_title(self):
        self.html.append('\n<h1>')

    def end_title(self):
        self.html.append('</h1>')

    def start_heading(self):
        self.html.append('\n<h2>')

    def end_heading(self):
        self.html.append('</h2>')

    def start_list(self):
        self.html.append('\n<ul>')
        self.inside_list = True

    def end_list(self):
        self.html.append('</ul>')
        self.inside_list = False

    def start_listitem(self):
        self.html.append('\n<li>')

    def end_listitem(self):
        self.html.append('</li>')

    def start_paragraph(self):
        if not self.inside_list:
            self.html.append('\n<p>')
        else:
            self.html.append('<p>')

    def end_paragraph(self):
        self.html.append('</p>')

    def feed(self, data):
        self.html.append(data)

    def sub_emphasis(self, match):
        return f'<em>{match.group(1)}</em>'

    def sub_url(self, match):
        url = match.group(1)
        return f'<a href="{url}">{url}</a>'

    def save(self, filename):
        full_html = ''.join(self.html)
        with open(filename, 'w', encoding='utf-8') as f:
            f.write(full_html)
        print(f"HTML 已保存到 {filename}")


# ======================== 4. 解析器 ========================
class Parser:
    def __init__(self, handler):
        self.handler = handler
        # 规则优先级：ListItem 必须在 List 之前
        self.rules = [
            TitleRule(),
            ListItemRule(),  # 列表项优先匹配
            ListRule(),  # 列表容器后匹配
            HeadingRule(),
            ParagraphRule()
        ]
        self.subs = [
            ('emphasis', re.compile(r'\*(.*?)\*')),
            ('url', re.compile(r'(http://[\w\./-]+)'))
        ]

    def parse(self, file):
        self.handler.start_document()
        for block in blocks(file):
            # 应用正则替换
            for sub_name, pattern in self.subs:
                substitution = self.handler.sub(sub_name)
                block = pattern.sub(substitution, block)
            # 应用规则匹配
            for rule in self.rules:
                if rule.condition(block):
                    if rule.action(block, self.handler):
                        break
        self.handler.end_document()


# ======================== 5. 运行测试 ========================
if __name__ == '__main__':
    handler = HTMLRender()
    parser = Parser(handler)
    # 使用标准测试文本
    with open('./data/test_input.txt', 'r', encoding='utf-8') as f:
        parser.parse(f)
    handler.save('output.html')